/**
 * 
 */
package PagedFile;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
 * @author Neil
 * It is a unit of buffer in the buffer pool.
 * One may get a buffer unit by allocating or getting a page from PF_FileHandle.
 * Once the upper layer got a Buffer, it may manipulate with its own will.
 * But, it should do the followings for noticing:
 * 1. When the buffer is not used recently, it's better to unpin it.
 *    One doesn't need to worry it will take a while.
 *    Actually, it does little. Write operation is done until necessary.
 * 2. Mark Modified when one wants to change the data in the buffer.
 *    Or else, the modification may not be written into the file.
 */
public class Buffer {
	public static final int BLOCK_SIZE = 4096;
	public static final int BLOCK_SHIFT = 12;
	
	public Block block;
	public ByteBuffer contents;
	public int modifiedBy;
	public long timeStamp;
	int pinCount;
	FileChannel fc;
	
	boolean read;
	
	public Buffer() {
		block = null;
		contents = ByteBuffer.allocateDirect(BLOCK_SIZE);
		pinCount = 0;
		modifiedBy = -1;
		timeStamp = -1;
		fc = null;
		read = false;
	}
	
	void pin() {
		++pinCount;
	}
	
	void unpin() {
		--pinCount;
	}
	
	boolean isPinned() {
		return pinCount>0;
	}
	
	public void mark(int txnum) {
		modifiedBy=txnum;
	}
	
	public boolean isModified() {
		return modifiedBy>0;
	}
	
	public synchronized void read() {
		if (read)
			return;
		if (this.fc == null) 
			throw new PF_Abort_Exception("Fatal");
		try {
			contents.clear();
			contents.rewind();
			fc.read(contents, block.pageNum<<BLOCK_SHIFT);
			timeStamp = System.currentTimeMillis();
		}catch(IOException e){
			throw new RuntimeException("Cannot read block " + block.pageNum + " from file " + block.filename);
		}
		read = true;
	}
	
	public synchronized void write() {
		if (this.fc == null)
			throw new PF_Abort_Exception("Fatal");
		try {
			if (modifiedBy>0) {
				contents.rewind();
				fc.write(contents, block.pageNum<<BLOCK_SHIFT);
				modifiedBy = -1;
				timeStamp = System.currentTimeMillis();
			}
		} catch(IOException e){
			throw new RuntimeException("Cannot write block " + block.pageNum + " from file " + block.filename);
		}
		read = true;
	}
}
